package com.escape.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.escape.auth.common.enums.AuthUserStatusEnum;
import com.escape.auth.common.enums.IsDeletedEnum;
import com.escape.auth.domain.constants.AuthConstant;
import com.escape.auth.domain.convert.AuthUserConverter;
import com.escape.auth.domain.entity.AuthUserBO;
import com.escape.auth.domain.redis.RedisUtil;
import com.escape.auth.domain.service.AuthUserDomainService;
import com.escape.auth.infra.basic.entity.*;
import com.escape.auth.infra.basic.service.*;
import com.google.gson.Gson;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.javassist.bytecode.CodeIterator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 用户信息表(AuthUser)表服务实现类
 *
 * @author makejava
 * @since 2025-01-09 11:31:50
 */
@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;
    @Resource
    private AuthRoleService authRoleService;
    @Resource
    private AuthUserRoleService authUserRoleService;
    @Resource
    private AuthRolePermissionService authRolePermissionService;
    @Resource
    private AuthPermissionService authPermissionService;

    private String key = "escape";
    private static final String LOGIN_PREFIX = "loginPrefix";

    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";
    private String authRolePrefix = "auth.role";

    /**
     * 用户注册
     */
    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {

        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainService.register.authUserBO: {}", JSON.toJSONString(authUserBO));
        }

        // 校验用户是否已存在
        AuthUser user = new AuthUser();
        user.setUserName(authUserBO.getUserName());
        List<AuthUser> authUserList = this.authUserService.queryByCondition(user);
        if (authUserList.size() > 0) {
            return true;
        }

        // 1. 添加用户
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToEntity(authUserBO);
        authUser.setIsDeleted(IsDeletedEnum.UN_DELETED.getCode());
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        if (!StringUtils.isBlank(authUserBO.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(),key));
        }
        Integer count = authUserService.insert(authUser);

        // 2. 添加用户、角色关联表
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstant.NORMAL_USER);
        AuthRole authRoleRes = this.authRoleService.queryByCondition(authRole);
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(authUser.getId());
        authUserRole.setRoleId(authRoleRes.getId());

        count += this.authUserRoleService.insert(authUserRole);

        // 3. 将 角色 与 权限 放入缓存
        String roleKey = this.redisUtil.buildKey(this.authRolePrefix, authUser.getUserName());
        LinkedList<AuthRole> authRoleList = new LinkedList<>();
        authRoleList.add(authRole);
        this.redisUtil.set(roleKey,new Gson().toJson(authRoleList));

        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(authRoleRes.getId());
        List<AuthRolePermission> authRolePermissionList = this.authRolePermissionService.queryByCondition(authRolePermission);
        List<Long> permissionIdList = authRolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());
        List<AuthPermission> authPermissionList = this.authPermissionService.queryByIdList(permissionIdList);
        String permissionKey = redisUtil.buildKey(this.authPermissionPrefix, authUser.getUserName());
        redisUtil.set(permissionKey, new Gson().toJson(authPermissionList));

        return count > 1;
    }

    /**
     * 更新用户信息
     */
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainService.update.bo: {}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToEntity(authUserBO);
        Integer count = this.authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 删除用户
     */
    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainService.delete.bo: {}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBO.getId());
        authUser.setIsDeleted(IsDeletedEnum.DELETED.getCode());
        Integer count = this.authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 用户启用/禁用
     */
    @Override
    public Boolean changeStatus(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainService.changeStatus.bo: {}", JSON.toJSONString(authUserBO));
        }
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToEntity(authUserBO);
        if ( AuthUserStatusEnum.OPEN.getCode() == authUser.getStatus()) {
            authUser.setStatus(AuthUserStatusEnum.CLOSE.getCode());
        }else{
            authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        }
        Integer count = this.authUserService.update(authUser);
        return count > 0;
    }

    /**
     * 验证码登录
     */
    @Override
    public SaTokenInfo doLogin(String validCode) {
        if (log.isInfoEnabled()) {
            log.info("AuthUserDomainService.doLogin.validCode: {}", validCode);
        }

        String key = redisUtil.buildKey(LOGIN_PREFIX, validCode);
        String openId = redisUtil.get(key);
        if (openId == null) {
            return null;
        }
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        Boolean isRegister = this.register(authUserBO);
        if (isRegister) {
            StpUtil.login(openId);
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            return tokenInfo;
        }
        return null;
    }
}
